package airAgent.group5;

import java.util.ArrayList;

import javax.sound.midi.SysexMessage;

import airAgent.group5.ontology.ontology.*;

import jade.content.Concept;
import jade.content.ContentElement;
import jade.content.lang.Codec;
import jade.content.lang.Codec.*;
import jade.content.lang.sl.*;
import jade.content.onto.*;
import jade.content.onto.basic.*;
//---
import jade.core.AID;
import jade.core.Agent;
import jade.core.behaviours.CyclicBehaviour;
import jade.core.behaviours.OneShotBehaviour;
import jade.core.behaviours.SimpleBehaviour;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import jade.tools.sniffer.Message;

public class Airline5 extends Agent {

    private static String NAME = "G5";
    airAgent.group5.ontology.ontology.Bank ontoBank = new airAgent.group5.ontology.ontology.Bank();
    private static String BANK = "BANK";
    private boolean hasBankAccount = false;
    private boolean hasTimer = false;
    private AID bankRef;
    private AID timerRef;
    private Codec codec = new SLCodec();
    private Ontology ontology = AirAgentOntology.getInstance();
    private ArrayList<Ticket> soldTickets = new ArrayList<Ticket>();
    private int canceledTickets = 0;
    private int flights = 0; //Flights != tickets != seats!
    private int today = 1;
    private int[] idas;
    private int[] vueltas;
    private float pasPerFli = 2.5f;
    private boolean syso = false; // debug output
    private boolean spam = false; // debug output
    
    private class CheckCompetitionBehaviour extends SimpleBehaviour {

        private boolean end = false;    	
    	
        public void action() {
            DFAgentDescription dfd = new DFAgentDescription();
            ServiceDescription sd = new ServiceDescription();
            sd.setType("AIRLINE");
            dfd.addServices(sd);
            try {
                // It finds agents of the required type
                DFAgentDescription[] res = new DFAgentDescription[1];
                res = DFService.search(myAgent, dfd);
                int competition = res.length;
                int clients = ConfigReader.getInstance().getClientsPerDay();
                pasPerFli = (float) clients / (float) competition;
                if (syso) System.out.println("********* clients = "+clients+ " // competition = "+competition);
                if (syso) System.out.println("********* PAS PER FLIGHT "+pasPerFli+" *********");
                end = true;
            } catch (Exception e) {
            }
        }
        public boolean done() {
            return end;
        }        
        
    }

    private class CreateAccountBehaviour extends SimpleBehaviour {

        private boolean end = false;
        private int tries = 0;

        public void action() {
            tries++;
          //  System.out.println(tries + " try to call for a bank! (Create Account)");
            // Creates the description for the type of agent to be searched
            DFAgentDescription dfd = new DFAgentDescription();
            ServiceDescription sd = new ServiceDescription();
            sd.setType(BANK);		// SHOULD USE THE ONTOLOGY (ontoBank)
            dfd.addServices(sd);

            try {
                // It finds agents of the required type
                DFAgentDescription[] res = new DFAgentDescription[1];
                res = DFService.search(myAgent, dfd);
                if (res.length > 0) {
                    bankRef = (AID) res[0].getName();
                    ACLMessage msg = new ACLMessage(ACLMessage.REQUEST);
                    msg.setContent(NAME);
                    msg.addReceiver(bankRef);
                    msg.setLanguage(codec.getName());
                    msg.setOntology(ontology.getName());
                    msg.setProtocol(AirAgentOntology.CREATEACCOUNT);
                    CreateAccount ca = new CreateAccount();
                    // As it is an action and the encoding language the SL, it must be wrapped
                    // into an Action
                    Action agAction = new Action(bankRef, ca);
                    try {
                        // The ContentManager transforms the java objects into strings
                        getContentManager().fillContent(msg, agAction);
                        send(msg);
                        end = true;
                        // System.out.println(getLocalName() + ": asks for an account");
                    } catch (CodecException ce) {
                        ce.printStackTrace();
                    } catch (OntologyException oe) {
                        oe.printStackTrace();
                    }

                    addBehaviour(new ResponseCreateAccountBehaviour());

                } else {
                    //  System.out.println("No bank found, wait 5 sekonds to procede");
                    doWait(5000);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

            if (tries == 3) {
                System.out.println("Couldn't find a bank in tree tries. Finishing!");
                end = true;
            }
        }

        public boolean done() {
            return end;
        }
    }

    private class ResponseCreateAccountBehaviour extends SimpleBehaviour {

        public void action() {
            ACLMessage msg = receive(MessageTemplate.and(MessageTemplate.MatchProtocol(AirAgentOntology.CREATEACCOUNT), MessageTemplate.MatchPerformative(ACLMessage.INFORM)));
            // ONLY INFORM ??? COULD BE FUCK OFF REFUSE OR NOT UNDERSTOOD
//                    doWait(1000);
            if (msg != null) {

                hasBankAccount = true;
                //System.out.println(getLocalName() + ": Woohoo I have a bank-account!");
                //System.out.println(getLocalName() + ": Woohoo I received an INFORM!");

            } else {
                block();
            }
        }

        @Override
        public boolean done() {
            return hasBankAccount();
        }
    }

    private class DepositBehaviour extends SimpleBehaviour {

        private boolean end = false;
        private int tries = 0;
        private float amount;
        //Cool idea, i did it somewhere else WAAAAY more complex :P

        public DepositBehaviour(float amount) {
            this.amount = amount;
        }

        public void action() {
            tries++;
            // System.out.println(tries + " try to call for a bank! (Deposit)");
            // Creates the description for the type of agent to be searched
            DFAgentDescription dfd = new DFAgentDescription();
            ServiceDescription sd = new ServiceDescription();
            sd.setType(BANK);		// SHOULD USE THE ONTOLOGY (ontoBank)
            dfd.addServices(sd);

            try {
                // It finds agents of the required type
                DFAgentDescription[] res = new DFAgentDescription[1];
                res = DFService.search(myAgent, dfd);
                if (res.length > 0) {
                    bankRef = (AID) res[0].getName();
                    ACLMessage msg = new ACLMessage(ACLMessage.REQUEST);
                    msg.setContent(NAME);
                    msg.addReceiver(bankRef);
                    msg.setLanguage(codec.getName());
                    msg.setOntology(ontology.getName());
                    msg.setProtocol(AirAgentOntology.DEPOSIT);
                    Deposit dep = new Deposit();
                    Money money = new Money();
                    money.setValue(amount);
                    dep.setAmount(money);
                    // As it is an action and the encoding language the SL, it must be wrapped
                    // into an Action
                    Action agAction = new Action(bankRef, dep);
                    try {
                        // The ContentManager transforms the java objects into strings
                        getContentManager().fillContent(msg, agAction);
                        send(msg);
                        end = true;
                        //   System.out.println(getLocalName() + ": asks to deposit money");
                    } catch (CodecException ce) {
                        ce.printStackTrace();
                    } catch (OntologyException oe) {
                        oe.printStackTrace();
                    }
                    /*
                    //               doWait(1000);
                    msg = receive(MessageTemplate.and(MessageTemplate.MatchProtocol(AirAgentOntology.DEPOSIT), MessageTemplate.MatchPerformative(ACLMessage.INFORM)));
                    // ONLY INFORM ??? COULD BE FUCK OFF REFUSE OR NOT UNDERSTOOD
                    //                    doWait(1000);

                    if (msg != null) {

                    if (msg.getSender().getName().equals(res[0].getName().getName())) {
                    System.out.println(getLocalName() + ": answer for deposit");
                    //System.out.println(getLocalName() + ": Woohoo I received an INFORM!");
                    end = true;
                    }

                    } else {

                    block();
                    }
                     *
                     */
                    addBehaviour(new ResponseDepositBehaviour());
                } else {
                    //  System.out.println("No bank found, wait 5 sekonds to procede");
                    doWait(5000);
                }
            } catch (Exception e) {
            }

            if (tries == 3) {
                System.out.println("Couldn't find a bank in tree tries. Finishing!");
                end = true;
            }
        }

        public boolean done() {
            return end;
        }
    }

    private class ResponseDepositBehaviour extends SimpleBehaviour {

        private boolean end = true;

        public void action() {
            ACLMessage msg = receive(MessageTemplate.and(MessageTemplate.MatchProtocol(AirAgentOntology.DEPOSIT), MessageTemplate.MatchPerformative(ACLMessage.INFORM)));
            // ONLY INFORM ??? COULD BE FUCK OFF REFUSE OR NOT UNDERSTOOD
//                    doWait(1000);
            if (msg != null) {
                //  System.out.println(getLocalName() + ": answer for deposit");
                end = true;
            } else {
                block();
            }
        }

        @Override
        public boolean done() {
            return end;
        }
    }

    private class WithdrawBehaviour extends SimpleBehaviour {

        private boolean end = false;
        private int tries = 0;
        private float amount;

        public WithdrawBehaviour(float amount) {
            this.amount = amount;
        }

        public void action() {
            tries++;
            //  System.out.println(tries + " try to call for a bank! (Withdraw)");
            // Creates the description for the type of agent to be searched
            DFAgentDescription dfd = new DFAgentDescription();
            ServiceDescription sd = new ServiceDescription();
            sd.setType(BANK);		// SHOULD USE THE ONTOLOGY  (ontoBank)
            dfd.addServices(sd);

            try {
                // It finds agents of the required type
                DFAgentDescription[] res = new DFAgentDescription[1];
                res = DFService.search(myAgent, dfd);
                if (res.length > 0) {
                    bankRef = (AID) res[0].getName();
                    ACLMessage msg = new ACLMessage(ACLMessage.REQUEST);
                    msg.setContent(NAME);
                    msg.addReceiver(bankRef);
                    msg.setLanguage(codec.getName());
                    msg.setOntology(ontology.getName());
                    msg.setProtocol(AirAgentOntology.WITHDRAWN);
                    Withdrawn wit = new Withdrawn();
                    Money money = new Money();
                    money.setValue(amount);
                    wit.setAmount(money);
                    // As it is an action and the encoding language the SL, it must be wrapped
                    // into an Action
                    Action agAction = new Action(bankRef, wit);
                    try {
                        // The ContentManager transforms the java objects into strings
                        getContentManager().fillContent(msg, agAction);
                        send(msg);
                        end = true;
                    //    System.out.println(getLocalName() + ": asks to withdraw money");
                    } catch (CodecException ce) {
                        ce.printStackTrace();
                    } catch (OntologyException oe) {
                        oe.printStackTrace();
                    }
                    addBehaviour(new ResponseWithdrawBehaviour());
                    /*
                    //        doWait(1000);
                    msg = receive(MessageTemplate.and(MessageTemplate.MatchProtocol(AirAgentOntology.WITHDRAWN), MessageTemplate.MatchPerformative(ACLMessage.INFORM)));
                    // ONLY INFORM ? COULD BE REFUSE OR NOT UNDERSTOOD
                    // doWait(1000);

                    if (msg != null) {

                    if (msg.getSender().getName().equals(res[0].getName().getName())) {
                    System.out.println(getLocalName() + ": answer for withdraw");
                    //System.out.println(getLocalName() + ": Woohoo I received an INFORM!");
                    end = true;
                    }

                    } else {
                    block();
                    }
                     * */
                } else {
                    //    System.out.println("No bank found, wait 5 sekonds to procede");
                    doWait(5000);
                }
            } catch (Exception e) {
            }

            if (tries == 3) {
                //   System.out.println("Couldn't find a bank in tree tries. Finishing!");
                end = true;
            }
        }

        public boolean done() {
            return end;
        }
    }

    private class ResponseWithdrawBehaviour extends SimpleBehaviour {

        private boolean end = false;

        public void action() {
            ACLMessage msg = receive(MessageTemplate.and(MessageTemplate.MatchProtocol(AirAgentOntology.WITHDRAWN), MessageTemplate.MatchPerformative(ACLMessage.INFORM)));
            // ONLY INFORM ??? COULD BE FUCK OFF REFUSE OR NOT UNDERSTOOD
//                    doWait(1000);
            if (msg != null) {
                //   System.out.println(getLocalName() + ": answer for withdraw");
                end = true;
            } else {
                block();
            }
        }

        @Override
        public boolean done() {
            return end;
        }
    }

    private class ResponseForTicketRequestBehaviour extends CyclicBehaviour {

        public void action() {

            ACLMessage msg = receive(MessageTemplate.MatchProtocol(AirAgentOntology.CHECKTICKETPRICE));

            if (msg != null) {
//            	System.out.println("fjhwqiugc");
                if (msg.getPerformative() == ACLMessage.QUERY_REF) {
                    try {
                        ContentElement ce = getContentManager().extractContent(msg);
                        // We expect an action inside the message
                        if (ce instanceof Action) {
                            Action agAction = (Action) ce;
                            Concept conc = agAction.getAction();
                            if (conc instanceof CheckTicketPrice) {
                                CheckTicketPrice desiredTicket = (CheckTicketPrice) conc;
                                //TODO: get info and check for a ticket that fix 'em
                                if(spam) System.out.println(getLocalName() + ": Received a ticket request from "
                                        + msg.getSender().getLocalName() + " for the " + desiredTicket.getTicketDate());
                                ACLMessage reply = msg.createReply();
                                // first check date-direction-seats availability
                                int date = desiredTicket.getTicketDate();
                                boolean direction = desiredTicket.getTicketDirection().getValue();
                                int seats = desiredTicket.getTicketSeats();
                                if(checkAvailability(date,direction,seats)) {
	                                Money money = new Money();
	                                money.setValue(calculatePrice());
	                                //Changed by Alex 30th May(and 4.6.), we wrap a ticket in a ReturnTicketPrice
	                                // We need more informations: Price, Date, Direction
	                                // See Protocol from 23.05. Request 4
	                                ReturnTicketPrice rtp = new ReturnTicketPrice();
	                                Ticket ticketPrice = new Ticket();
	                                ticketPrice.setTicketPrice(money);
	                                ticketPrice.setTicketAirlineID(getAID());
	                                //ticketPrice.setTicketClientID(msg.getSender()); //Must be changed in the Ontology
	                                ticketPrice.setTicketDirection(desiredTicket.getTicketDirection());
	                                ticketPrice.setTicketDate(desiredTicket.getTicketDate());
	                                rtp.setPrice(ticketPrice);
	                                
	                                reply.setPerformative(ACLMessage.INFORM);
	                                reply.setProtocol(AirAgentOntology.RETURNTICKETPRICE);
	                                Action rtpAction = new Action(msg.getSender(), rtp);                                 
	                                getContentManager().fillContent(reply, rtpAction);
	                                send(reply);
	                                if(spam) System.out.println(getLocalName() + ": " + msg.getSender().getLocalName() + " asks for a ticket! Returned price: " + money.getValue());
                                } else {
                                    reply.setPerformative(ACLMessage.REFUSE);
                                    reply.setProtocol(AirAgentOntology.RETURNTICKETPRICE);
                                    send(reply);
                                    if(spam) System.out.println(getLocalName() + ": " + msg.getSender().getLocalName() + " asks for a ticket! REFUSEEED");
                                }
                            } else {
                            	System.err.println(getLocalName() + ": ResponseForTicketRequestBehaviour ERROR -> CheckTicketPrice expected!");
                            }
                        }
                    } catch (CodecException e) {
                        e.printStackTrace();
                    } catch (OntologyException oe) {
                        oe.printStackTrace();
                    }
                } else {
                    System.err.println(getLocalName() + ": ResponseForTicketRequestBehaviour ERROR -> QuerryRef expected!");
                }

            } else {
                block();
            }
        }
    }

    private class ResponseForCancelTicketBehaviour extends CyclicBehaviour{
    	
    	private void freeSeat(int date, boolean direction, int seats) {   		
    		if(direction) {
    			idas[date-1] += seats;
    		} else {
    			vueltas[date-1] += seats;
    		}
    	}
    
    	private Ticket findTicket(CancelTicket cancelTicket){
    		for(Ticket t:soldTickets){
        		//Check if a ticket is equal in date, direction, seats.
        		// If the ontology change, we can check the msg.getSender() agains the t.getClientAID()
    			if((t.getTicketDate() == (cancelTicket.getTicketDate()))){
        			if((t.getTicketDirection().getValue() == (cancelTicket.getTicketDirection().getValue()))){
        				if((t.getTicketSeats() == cancelTicket.getTicketSeats())){
        					return t;
        				}
        			}
        		}
        	
    		}
    		return null;
    	}
    	
		public void action() {
			ACLMessage msg = receive(MessageTemplate.MatchProtocol(AirAgentOntology.CANCELTICKET));
			if(msg != null){
				if(spam) System.out.println(getLocalName() + ": " + msg.getSender().getLocalName() + " trys to cancel a ticket!");
				if(soldTickets.size() != 0){
					
	                if (msg.getPerformative() == ACLMessage.REQUEST) {
	                    try {
	                        ContentElement ce = getContentManager().extractContent(msg);
	                        // We expect an action inside the message
	                        if (ce instanceof Action) {
	                            Action agAction = (Action) ce;
	                            Concept conc = agAction.getAction();
	                            // If the action is CreateAccount...
	                            if (conc instanceof CancelTicket) {
	                            	CancelTicket cancelTicket = (CancelTicket) conc;
	                                //for the moment we just answer with an inform
	                            	//TODO: Check which ticket to cancel!
	                            	Ticket foundTicket = findTicket(cancelTicket);
	                            	ACLMessage reply = msg.createReply();
	                            	if(soldTickets.remove(foundTicket)){
	                            		int date = cancelTicket.getTicketDate();
	                                    boolean direction = cancelTicket.getTicketDirection().getValue();
	                                    int seats = cancelTicket.getTicketSeats();	                            		
	                            		if(date > today) {         		
	                            			if(spam) System.out.println(getLocalName() + ": CancelTicket -> Ticket for the  " + foundTicket.getTicketDate() + " is canceled and deleted from the ticket list.");
		                            		reply.setPerformative(ACLMessage.INFORM);
		                            		Action returnCancelTicketAction = new Action(msg.getSender(), foundTicket);
		                            		getContentManager().fillContent(reply, returnCancelTicketAction);
		                            		canceledTickets++;
		                            		
		                                    freeSeat(date, direction, seats);
		                            		withdrawMoney(ConfigReader.getInstance().getTaxes()*seats);
			                                if (direction) {
			                                	if(syso) System.out.println("@@@@@@@ GO TICKET CANCELED DAY "+date);
			                                } else {
			                                	if(syso) System.out.println("@@@@@@@ RETURN TICKET CANCELED DAY "+date);
			                                }
	                            		} else { // intentan jodernos
	                            			if(spam) System.out.println(getLocalName() + ": CancelTicket -> Ticket for today REFUSE!");
	                                        reply.setPerformative(ACLMessage.REFUSE);                            			
	                            		}
	                            	}
	                            	else{
	                            		if(spam) System.out.println(getLocalName() + ": CancelTicket -> Ticket not found REFUSE!");
	                            		reply.setPerformative(ACLMessage.REFUSE);
	                            	}
	                            	send(reply);
	                            }
	                        }
	                    } catch (CodecException e) {
	                        e.printStackTrace();
	                    } catch (OntologyException oe) {
	                        oe.printStackTrace();
	                    }
	                } else {
	                    block();
	                }
					
					
				}
				else{
					if(spam) System.out.println(getLocalName() + ": CancelTicket -> No tickets available to cancel!");
				}
			}
		}
    }

    private class ResponseForBuyTicketBehaviour extends CyclicBehaviour {

        private void occupySeat(int date, boolean direction, int seats) {
            if (direction) {
                idas[date-1] -= seats;
            } else {
                vueltas[date-1] -= seats;
            }
        }

        public void action() {
            ACLMessage msg = receive(MessageTemplate.MatchProtocol(AirAgentOntology.BUYTICKET));
            if (msg != null) {
                if (msg.getPerformative() == ACLMessage.REQUEST) {
                    try {
                        ContentElement ce = getContentManager().extractContent(msg);
                        // We expect an action inside the message
                        if (ce instanceof Action) {
                            Action agAction = (Action) ce;
                            Concept conc = agAction.getAction();
                            // If the action is CreateAccount...
                            if (conc instanceof BuyTicket) {
                                //WARNING!
                            	/*
                                 * Due to group 1, we don't have to send a ticket. Nevertheless, some groups
                                 * expect a ticket in the reply. Because the ticket is not originaly ment to be
                                 * send, the groups don't care that the client in the ticket is a "Client" object.
                                 * This object doesn't make sense and should be a AID. This is confusing!
                                 */
                                ACLMessage reply = msg.createReply();
                                BuyTicket bTicket = (BuyTicket) conc;
                                // first check date-direction-seats availability
                                int date = bTicket.getTicketDate();
                                boolean direction = bTicket.getTicketDirection().getValue();
                                int seats = bTicket.getTicketSeats();
                                if (checkAvailability(date, direction, seats)) {
                                    //for the moment we just answer with an inform
                                    Ticket ticket = new Ticket();
                                    ticket.setTicketDate(bTicket.getTicketDate());
                                    ticket.setTicketDirection(bTicket.getTicketDirection());
                                    ticket.setTicketSeats(bTicket.getTicketSeats());
                                    ticket.setTicketAirlineID(getAID());
                                    ticket.setTicketClientID(msg.getSender());                                	
                  	              
	                                //This is still not solved, sending the ticket or the BuyTicket?
	                                Action returnTicketAction = new Action(msg.getSender(), bTicket);
	
	                                reply.setPerformative(ACLMessage.INFORM);
	                                getContentManager().fillContent(reply, returnTicketAction);
	
	                                if(spam) System.out.println(getLocalName() + ": sent inform msg to client: " + msg.getSender().getLocalName() + " when buying a ticket (" + ticket.getTicketDate() + ")");
	                                send(reply);
	                                
	                                soldTickets.add(ticket);//Save the ticket
	                                
	                                occupySeat(date, direction, seats);                 
	                                float price = calculatePrice()*seats;
	                                depositMoney(price);
	                                String fNum = (date < 10) ? "0"+date : ""+date;
	                                if (direction) {                                	
	                                	System.out.println(getLocalName() + ": "+seats+" tickets sold"+
	                                			" for flight G5"+fNum+"01 for "+price+" euros");
	                                } else {
	                                	System.out.println(getLocalName() + ": "+seats+" tickets sold"+
	                                			" for flight G5"+fNum+"02 for "+price+" euros");
	                                }
                                } else {
                                    reply.setPerformative(ACLMessage.REFUSE);
                                    send(reply);
                                    //  System.out.println(getLocalName() + ": sent REFUSE msg to client: " + msg.getSender().getLocalName() + " when buying a ticket (" + ticket.getTicketDate() + ")");
                                }
                            }
                        }
                    } catch (CodecException e) {
                        e.printStackTrace();
                    } catch (OntologyException oe) {
                        oe.printStackTrace();
                    }
                } else {
                    block();
                }
            }
        }
    }

    private class SubscribeTimerBehaviour extends CyclicBehaviour {

        private boolean end = false;
        private int tries = 0;

        public void action() {

            // Creates the description for the type of agent to be searched
            DFAgentDescription dfd = new DFAgentDescription();
            ServiceDescription sd = new ServiceDescription();
            sd.setType(AirAgentOntology.TIMER);		// SHOULD USE THE ONTOLOGY  (ontoBank)
            dfd.addServices(sd);
            if (hasTimer()) {
                removeBehaviour(this);
            } else {
                try {
                    tries++;
                    //   System.out.println(tries + " tries to subscribe timer");
                    // It finds agents of the required type
                    DFAgentDescription[] res = new DFAgentDescription[1];
                    res = DFService.search(myAgent, dfd);
                    if (res.length > 0) {
                        timerRef = (AID) res[0].getName();
                        ACLMessage msg = new ACLMessage(ACLMessage.SUBSCRIBE);
                        msg.addReceiver(timerRef);
                        msg.setLanguage(codec.getName());
                        msg.setOntology(ontology.getName());
                        msg.setProtocol(AirAgentOntology.SUBSCRIBETIMER);
                        SubscribeTimer subscribeTimer = new SubscribeTimer();
                        // As it is an action and the encoding language the SL, it must be wrapped
                        // into an Action
                        Action agAction = new Action(timerRef, subscribeTimer);
                        try {
                            // The ContentManager transforms the java objects into strings
                            getContentManager().fillContent(msg, agAction);
                            send(msg);
                            end = true;
                            //    System.out.println(getLocalName() + ": wants to subscribe to timer");
                        } catch (CodecException ce) {
                            ce.printStackTrace();
                        } catch (OntologyException oe) {
                            oe.printStackTrace();
                        }
                        hasTimer = true;
                        addBehaviour(new ResponseSubscribeTimerBehaviour());
                    }
                } catch (Exception e) {
                }
            }
        }
    }

    private class ResponseSubscribeTimerBehaviour extends SimpleBehaviour {

        @Override
        public void action() {
            ACLMessage msg = receive(MessageTemplate.MatchProtocol(AirAgentOntology.SUBSCRIBETIMER));
            if (msg != null) {
                if (msg.getPerformative() == ACLMessage.AGREE) {
                    hasTimer = true;
                  //  System.out.println(getLocalName() + ": Subscribed to Timer");
                }
            } else {
                block();
            }

        }

        @Override
        public boolean done() {
            return hasTimer();
        }
    }

    private class WaitToBehaviour extends SimpleBehaviour {

        private boolean end = false;

        public void action() {
            ACLMessage msg = receive(MessageTemplate.MatchProtocol(AirAgentOntology.STARTSIMULATION));

            if (msg != null) {
 //               System.out.println(getLocalName() + ": Starts...");
                end = true;
//                depositMoney(100);   //TODO check if hasbankaccount before call any of these methods
//                withdrawMoney(100);
                responseForTicket();
                responseForBuyTicket();
            } else {
                block();
            }
        }

        @Override
        public boolean done() {
            return end;
        }
    }

    private class EndSimlationBehaviour extends SimpleBehaviour {

        boolean end = false;

        public void action() {
            ACLMessage msg = receive(MessageTemplate.MatchProtocol(AirAgentOntology.ENDOFSIMULATION));

            if (msg != null) {
                //  System.out.println(getLocalName() + ": Ending...");
//                flights = 24;//Just for testing

                int quantityOftickets = soldTickets.size();
                float subsidy = ConfigReader.getInstance().getSubsidy();
                int divBy5 = flights / 5; //indirect cast from float to integer, will cut away the rest. Is ok in this case
                float totalSubsidy = subsidy * divBy5;
                depositMoney(totalSubsidy);

                //Put the bonus on the bank!
                System.err.println("COMPANY: G5");
                System.err.println("Flights performed: " + flights);
                int fliCanc = 58-flights;
                System.err.println("Flights cancelled: " + fliCanc);
                // System.err.println("// There fore received bonus of = " + totalBonus);
                System.err.println("Tickets sold: " + quantityOftickets);
                System.err.println("Tickets cancelled: " + canceledTickets);

                end = true;
            } else {
                block();
            }
        }

        @Override
        public boolean done() {
            //for now just call takeDown, but should be remove all behaviour and inform monitor
            // takeDown(); //Ohhh not here! done() is called all the time :)
            return end;
        }
    }

    private class InformDayBehaviour extends CyclicBehaviour {

        public void action() {
            ACLMessage msg = receive(MessageTemplate.MatchProtocol(AirAgentOntology.INFORMDATE));

            if (msg != null) {
                if (msg.getPerformative() == ACLMessage.INFORM) {
                	try {
						ContentElement ce = getContentManager().extractContent(msg);
						if (ce instanceof Action) {
							Action agAction = (Action) ce;
	                    	Concept conc = agAction.getAction();
	                    	if (conc instanceof InformDate){
	                    		InformDate id = (InformDate) conc;
	                    		
	                    		// NEW DAY
	                    		today = id.getDate();
	                    		
	                    		if (today > 1) {
		                    		String fNum = (today < 10) ? "0"+today : ""+today;
		                    		
		                    		// MORNING
		                    		if(idas[today-1] < 5) {	// we're flyin to Montegancedo
		                    			flights++;
		                    			withdrawMoney(ConfigReader.getInstance().getFuel());
		                    			System.out.println(getLocalName() + ": flight G5"+fNum+"01 departs");
		                    		} else {
		                    			System.out.println(getLocalName() + ": flight G5"+fNum+"01 cancelled");	
		                    		}
		                    		
		                    		// AFTERNOON
		                    		if(vueltas[today-1] < 5) {	// we're flyin to Barajas
		                    			flights++;
		                    			withdrawMoney(ConfigReader.getInstance().getFuel());
		                    			System.out.println(getLocalName() + ": flight G5"+fNum+"02 departs");
		                    		} else {
		                    			System.out.println(getLocalName() + ": flight G5"+fNum+"02 cancelled");	
		                    		}     
	                    		} else if(today == 1) {
	                    			// check number of airlines
	                    			addBehaviour(new CheckCompetitionBehaviour());
	                    		}
	                    		if (today == 2) {
	                    			
	                    		}
	                    	}
						}
                    } catch (CodecException e) {
                        e.printStackTrace();
                    } catch (OntologyException oe) {
                        oe.printStackTrace();
                    }
                }

            } else {
                block();
            }
        }
    }

    protected void setup() {
        //System.out.println(getLocalName() + ": HAS ENTERED");
        System.out.println("G5: enters into the system");
        getContentManager().registerLanguage(codec);
        getContentManager().registerOntology(ontology);
        try {
            // Creates its own description
            DFAgentDescription dfd = new DFAgentDescription();
            ServiceDescription sd = new ServiceDescription();
            sd.setName(this.getName());
            sd.setType("AIRLINE");
            dfd.addServices(sd);
            // Registers its description in the DF
            DFService.register(this, dfd);         
            // System.out.println(getLocalName() + ": registered in the DF");
        } catch (FIPAException e) {
            e.printStackTrace();
        }

        idas = prepareFlightArray();
        vueltas = prepareFlightArray();

//		BEHAVIOURS ****************************************************************

        // Adds a behavior to search a bank agent and request a bank account

        addBehaviour(new CreateAccountBehaviour());
        addBehaviour(new SubscribeTimerBehaviour());
        addBehaviour(new WaitToBehaviour());
        addBehaviour(new InformDayBehaviour());
        addBehaviour(new EndSimlationBehaviour());
        addBehaviour(new ResponseForCancelTicketBehaviour());
        addBehaviour(new EndSimlationBehaviour());
        //  doWait(1000);       // this should be removed

        withdrawMoney(ConfigReader.getInstance().getPilot()); // pay that bitch in the beginning
        withdrawMoney(ConfigReader.getInstance().getHelicopter());
    }

    private int[] prepareFlightArray() {
        int days = ConfigReader.getInstance().getSimulationDays();
        int[] flightArray = new int[days];
        for (int i = 0; i < days; i++) {
        	flightArray[i] = 5;
        }
        return flightArray;
    }

	private boolean checkAvailability(int date, boolean direction, int seats) {
    	// direction: true-> ida / false -> vuelta
		
		if (date >= ConfigReader.getInstance().getSimulationDays()) {
			if(syso) System.out.println("||||||| Date "+date+" is out of simulation");
			return false; // intentan jodernos
		}
		if (date <= today) {
			if(syso) System.out.println("||||||| Date "+date+" is today ( "+today+ ") o lower");
			return false; // intentan jodernos
		}
		if (seats > 5) {
			if(syso) System.out.println("||||||| Seats "+seats+" is more than 5");
			return false; // intentan jodernos
		}
		if (seats < 1) {
			if(syso) System.out.println("||||||| Seats "+seats+" is less than 1");
			return false; // intentan jodernos
		}
		
		if(direction) {
			if(syso) System.out.println("||||||| check GO ticket, DAY "+ date+", "+seats+" seats / "+idas[date-1]+" free");
			if (idas[date-1] >= seats) return true;
		} else {
			if(syso) System.out.println("||||||| check RETURN ticket, DAY "+ date+", "+seats+" seats / "+vueltas[date-1]+" free");
			if (vueltas[date-1] >= seats) return true; 
		}
		if(syso) System.out.println("||||||| FULL -> REFUSED");
		return false;
	}
	
	private float calculatePrice() {
		// price per flight can be changed in every day change
		// could depend on date, buyin in advance is cheaper
		// but why?
		
		float profit = 0.0f; // profit per passenger    		
		//----------------------
		float taxes = ConfigReader.getInstance().getTaxes();
		float fuel = ConfigReader.getInstance().getFuel();
		float helicop = ConfigReader.getInstance().getHelicopter();
		float pilot = ConfigReader.getInstance().getPilot();
		//----------------------
		float fixed = (helicop + pilot) / (29 * 2); // fixed cost per flight
		float costPerPassenger = (fixed + fuel) / pasPerFli;
		//----------------------	
		// if we don't sell, we're screwed !
		// if we sell a lot, we have profit from subsidy and keepin fuel money from cancellations
		// checkin prices of our competition ? NO
		return costPerPassenger + taxes + profit;
	} 
	
    protected void takeDown() {
        // System.out.println(getLocalName() + ": Goodbye");
    }

    public void depositMoney(float amount) {
        addBehaviour(new DepositBehaviour(amount));
    }

    public void withdrawMoney(float amount) {
        addBehaviour(new WithdrawBehaviour(amount));
    }

    public void responseForTicket() {
        addBehaviour(new ResponseForTicketRequestBehaviour());
    }

    public void responseForBuyTicket() {
        addBehaviour(new ResponseForBuyTicketBehaviour());
    }

    public boolean hasBankAccount() {
        return hasBankAccount;
    }

    public boolean hasTimer() {
        return hasTimer;
    }
}
